/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2009 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#include "OgreStableHeaders.h"

#include "OgreParticleEmitterEx.h"
#include "OgreParticleEmitterFactoryEx.h"

namespace Ogre
{
	// Define static members
    EmitterCommandsEx::CmdAngle ParticleEmitterEx::msAngleCmd;
    EmitterCommandsEx::CmdColour ParticleEmitterEx::msColourCmd;
    EmitterCommandsEx::CmdColourRangeStart ParticleEmitterEx::msColourRangeStartCmd;
    EmitterCommandsEx::CmdColourRangeEnd ParticleEmitterEx::msColourRangeEndCmd;
    EmitterCommandsEx::CmdDirection ParticleEmitterEx::msDirectionCmd;
    EmitterCommandsEx::CmdEmissionRate ParticleEmitterEx::msEmissionRateCmd;
    EmitterCommandsEx::CmdMaxTTL ParticleEmitterEx::msMaxTTLCmd;
    EmitterCommandsEx::CmdMaxVelocity ParticleEmitterEx::msMaxVelocityCmd;
    EmitterCommandsEx::CmdMinTTL ParticleEmitterEx::msMinTTLCmd;
    EmitterCommandsEx::CmdMinVelocity ParticleEmitterEx::msMinVelocityCmd;
    EmitterCommandsEx::CmdPosition ParticleEmitterEx::msPositionCmd;
    EmitterCommandsEx::CmdTTL ParticleEmitterEx::msTTLCmd;
    EmitterCommandsEx::CmdVelocity ParticleEmitterEx::msVelocityCmd;
    EmitterCommandsEx::CmdDuration ParticleEmitterEx::msDurationCmd;
    EmitterCommandsEx::CmdMinDuration ParticleEmitterEx::msMinDurationCmd;
    EmitterCommandsEx::CmdMaxDuration ParticleEmitterEx::msMaxDurationCmd;
    EmitterCommandsEx::CmdRepeatDelay ParticleEmitterEx::msRepeatDelayCmd;
    EmitterCommandsEx::CmdMinRepeatDelay ParticleEmitterEx::msMinRepeatDelayCmd;
    EmitterCommandsEx::CmdMaxRepeatDelay ParticleEmitterEx::msMaxRepeatDelayCmd;
    EmitterCommandsEx::CmdName ParticleEmitterEx::msNameCmd;
    EmitterCommandsEx::CmdEmittedEmitter ParticleEmitterEx::msEmittedEmitterCmd;


    //-----------------------------------------------------------------------
    ParticleEmitterEx::ParticleEmitterEx(ParticleSystemEx* psys)
      : mParent(psys),
        mStartTime(0),
        mDurationMin(0),
        mDurationMax(0),
        mDurationRemain(0),
        mRepeatDelayMin(0),
        mRepeatDelayMax(0),
        mRepeatDelayRemain(0)
    {

        // Reasonable defaults
        mAngle = 0;
        setDirection(Vector3::UNIT_X);
        mEmissionRate = 10;
        mMaxSpeed = mMinSpeed = 1;
        mMaxTTL = mMinTTL = 5;
        mPosition = Vector3::ZERO;
        mColourRangeStart = mColourRangeEnd = ColourValue::White;
        mEnabled = true;
        mRemainder = 0;
		mName = StringUtil::BLANK;
		mEmittedEmitter = StringUtil::BLANK;
		mEmitted = false;
    }
    //-----------------------------------------------------------------------
    ParticleEmitterEx::~ParticleEmitterEx() 
    {
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setPosition(const Vector3& pos) 
    { 
        mPosition = pos; 
    }
    //-----------------------------------------------------------------------
    const Vector3& ParticleEmitterEx::getPosition(void) const 
    { 
        return mPosition; 
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setDirection(const Vector3& inDirection) 
    { 
        mDirection = inDirection; 
        mDirection.normalise();
        // Generate an up vector (any will do)
        mUp = mDirection.perpendicular();
        mUp.normalise();
    }
    //-----------------------------------------------------------------------
    const Vector3& ParticleEmitterEx::getDirection(void) const
    { 
        return mDirection; 
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setAngle(const Radian& angle)
    {
        // Store as radians for efficiency
        mAngle = angle;
    }
    //-----------------------------------------------------------------------
    const Radian& ParticleEmitterEx::getAngle(void) const
    {
        return mAngle;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setParticleVelocity(Real speed)
    {
        mMinSpeed = mMaxSpeed = speed;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setParticleVelocity(Real min, Real max)
    {
        mMinSpeed = min;
        mMaxSpeed = max;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setEmissionRate(Real particlesPerSecond) 
    { 
        mEmissionRate = particlesPerSecond; 
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getEmissionRate(void) const 
    { 
        return mEmissionRate; 
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setTimeToLive(Real ttl)
    {
        mMinTTL = mMaxTTL = ttl;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setTimeToLive(Real minTtl, Real maxTtl)
    {
        mMinTTL = minTtl;
        mMaxTTL = maxTtl;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setColour(const ColourValue& inColour)
    {
        mColourRangeStart = mColourRangeEnd = inColour;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setColour(const ColourValue& colourStart, const ColourValue& colourEnd)
    {
        mColourRangeStart = colourStart;
        mColourRangeEnd = colourEnd;
    }
	//-----------------------------------------------------------------------
    const String& ParticleEmitterEx::getName(void) const
    {
        return mName;
    }
	//-----------------------------------------------------------------------
    void ParticleEmitterEx::setName(const String& newName)
    {
		mName = newName;
    }
	//-----------------------------------------------------------------------
    const String& ParticleEmitterEx::getEmittedEmitter(void) const
    {
        return mEmittedEmitter;
    }
	//-----------------------------------------------------------------------
    void ParticleEmitterEx::setEmittedEmitter(const String& emittedEmitter)
    {
        mEmittedEmitter = emittedEmitter;
    }
	//-----------------------------------------------------------------------
	bool ParticleEmitterEx::isEmitted(void) const
    {
		return mEmitted;
    }
	//-----------------------------------------------------------------------
	void ParticleEmitterEx::setEmitted(bool emitted)
    {
        mEmitted = emitted;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::genEmissionDirection(Vector3& destVector)
    {
        if (mAngle != Radian(0))
        {
            // Randomise angle
            Radian angle = Math::UnitRandom() * mAngle;

            // Randomise direction
            destVector = mDirection.randomDeviant(angle, mUp);
        }
        else
        {
            // Constant angle
            destVector = mDirection;
        }

        // Don't normalise, we can assume that it will still be a unit vector since
        // both direction and 'up' are.

    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::genEmissionVelocity(Vector3& destVector)
    {
        Real scalar;
        if (mMinSpeed != mMaxSpeed)
        {
            scalar = mMinSpeed + (Math::UnitRandom() * (mMaxSpeed - mMinSpeed));
        }
        else
        {
            scalar = mMinSpeed;
        }

        destVector *= scalar;
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::genEmissionTTL(void)
    {
        if (mMaxTTL != mMinTTL)
        {
            return mMinTTL + (Math::UnitRandom() * (mMaxTTL - mMinTTL));
        }
        else
        {
            return mMinTTL;
        }
    }
    //-----------------------------------------------------------------------
    unsigned short ParticleEmitterEx::genConstantEmissionCount(Real timeElapsed)
    {
        unsigned short intRequest;
        
        if (mEnabled)
        {
            // Keep fractions, otherwise a high frame rate will result in zero emissions!
            mRemainder += mEmissionRate * timeElapsed;
            intRequest = (unsigned short)mRemainder;
            mRemainder -= intRequest;

            // Check duration
            if (mDurationMax)
            {
                mDurationRemain -= timeElapsed;
                if (mDurationRemain <= 0) 
                {
                    // Disable, duration is out (takes effect next time)
                    setEnabled(false);
                }
            }
            return intRequest;
        }
        else
        {
            // Check repeat
            if (mRepeatDelayMax)
            {
                mRepeatDelayRemain -= timeElapsed;
                if (mRepeatDelayRemain <= 0)
                {
                    // Enable, repeat delay is out (takes effect next time)
                    setEnabled(true);
                }
            }
            if(mStartTime)
            {
                mStartTime -= timeElapsed;
                if(mStartTime <= 0)
                {
                    setEnabled(true);
                    mStartTime = 0;
                }
            }
            return 0;
        }

    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::genEmissionColour(ColourValue& destColour)
    {
        if (mColourRangeStart != mColourRangeEnd)
        {
            // Randomise
            //Real t = Math::UnitRandom();
            destColour.r = mColourRangeStart.r + (Math::UnitRandom() * (mColourRangeEnd.r - mColourRangeStart.r));
            destColour.g = mColourRangeStart.g + (Math::UnitRandom() * (mColourRangeEnd.g - mColourRangeStart.g));
            destColour.b = mColourRangeStart.b + (Math::UnitRandom() * (mColourRangeEnd.b - mColourRangeStart.b));
            destColour.a = mColourRangeStart.a + (Math::UnitRandom() * (mColourRangeEnd.a - mColourRangeStart.a));
        }
        else
        {
            destColour = mColourRangeStart;
        }
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::addBaseParameters(void)    
    {
        ParamDictionary* dict = getParamDictionary();

        dict->addParameter(ParameterDef("angle", 
            "The angle up to which particles may vary in their initial direction "
            "from the emitters direction, in degrees." , PT_REAL),
            &msAngleCmd);

        dict->addParameter(ParameterDef("colour", 
            "The colour of emitted particles.", PT_COLOURVALUE),
            &msColourCmd);

        dict->addParameter(ParameterDef("colour_range_start", 
            "The start of a range of colours to be assigned to emitted particles.", PT_COLOURVALUE),
            &msColourRangeStartCmd);

        dict->addParameter(ParameterDef("colour_range_end", 
            "The end of a range of colours to be assigned to emitted particles.", PT_COLOURVALUE),
            &msColourRangeEndCmd);

        dict->addParameter(ParameterDef("direction", 
            "The base direction of the emitter." , PT_VECTOR3),
            &msDirectionCmd);

        dict->addParameter(ParameterDef("emission_rate", 
            "The number of particles emitted per second." , PT_REAL),
            &msEmissionRateCmd);

        dict->addParameter(ParameterDef("position", 
            "The position of the emitter relative to the particle system center." , PT_VECTOR3),
            &msPositionCmd);

        dict->addParameter(ParameterDef("velocity", 
            "The initial velocity to be assigned to every particle, in world units per second." , PT_REAL),
            &msVelocityCmd);

        dict->addParameter(ParameterDef("velocity_min", 
            "The minimum initial velocity to be assigned to each particle." , PT_REAL),
            &msMinVelocityCmd);

        dict->addParameter(ParameterDef("velocity_max", 
            "The maximum initial velocity to be assigned to each particle." , PT_REAL),
            &msMaxVelocityCmd);

        dict->addParameter(ParameterDef("time_to_live", 
            "The lifetime of each particle in seconds." , PT_REAL),
            &msTTLCmd);

        dict->addParameter(ParameterDef("time_to_live_min", 
            "The minimum lifetime of each particle in seconds." , PT_REAL),
            &msMinTTLCmd);

        dict->addParameter(ParameterDef("time_to_live_max", 
            "The maximum lifetime of each particle in seconds." , PT_REAL),
            &msMaxTTLCmd);

        dict->addParameter(ParameterDef("duration", 
            "The length of time in seconds which an emitter stays enabled for." , PT_REAL),
            &msDurationCmd);

        dict->addParameter(ParameterDef("duration_min", 
            "The minimum length of time in seconds which an emitter stays enabled for." , PT_REAL),
            &msMinDurationCmd);

        dict->addParameter(ParameterDef("duration_max", 
            "The maximum length of time in seconds which an emitter stays enabled for." , PT_REAL),
            &msMaxDurationCmd);

        dict->addParameter(ParameterDef("repeat_delay", 
            "If set, after disabling an emitter will repeat (reenable) after this many seconds." , PT_REAL),
            &msRepeatDelayCmd);

        dict->addParameter(ParameterDef("repeat_delay_min", 
            "If set, after disabling an emitter will repeat (reenable) after this minimum number of seconds." , PT_REAL),
            &msMinRepeatDelayCmd);

        dict->addParameter(ParameterDef("repeat_delay_max", 
            "If set, after disabling an emitter will repeat (reenable) after this maximum number of seconds." , PT_REAL),
            &msMaxRepeatDelayCmd);

		dict->addParameter(ParameterDef("name", 
			"This is the name of the emitter" , PT_STRING),
			&msNameCmd);
		
		dict->addParameter(ParameterDef("emit_emitter", 
			"If set, this emitter will emit other emitters instead of visual particles" , PT_STRING),
			&msEmittedEmitterCmd);
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getParticleVelocity(void) const
    {
        return mMinSpeed;
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getMinParticleVelocity(void) const
    {
        return mMinSpeed;
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getMaxParticleVelocity(void) const
    {
        return mMaxSpeed;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setMinParticleVelocity(Real min)
    {
        mMinSpeed = min;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setMaxParticleVelocity(Real max)
    {
        mMaxSpeed = max;
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getTimeToLive(void) const
    {
        return mMinTTL;
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getMinTimeToLive(void) const
    {
        return mMinTTL;
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getMaxTimeToLive(void) const
    {
        return mMaxTTL;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setMinTimeToLive(Real min)
    {
        mMinTTL = min;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setMaxTimeToLive(Real max)
    {
        mMaxTTL = max;
    }
    //-----------------------------------------------------------------------
    const ColourValue& ParticleEmitterEx::getColour(void) const
    {
        return mColourRangeStart;
    }
    //-----------------------------------------------------------------------
    const ColourValue& ParticleEmitterEx::getColourRangeStart(void) const
    {
        return mColourRangeStart;
    }
    //-----------------------------------------------------------------------
    const ColourValue& ParticleEmitterEx::getColourRangeEnd(void) const
    {
        return mColourRangeEnd;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setColourRangeStart(const ColourValue& val)
    {
        mColourRangeStart = val;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setColourRangeEnd(const ColourValue& val )
    {
        mColourRangeEnd = val;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setEnabled(bool enabled)
    {
        mEnabled = enabled;
        // Reset duration & repeat
        initDurationRepeat();
    }
    //-----------------------------------------------------------------------
    bool ParticleEmitterEx::getEnabled(void) const
    {
        return mEnabled;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setStartTime(Real startTime)
    {
        setEnabled(false);
        mStartTime = startTime;
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getStartTime(void) const
    {
        return mStartTime;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setDuration(Real duration)
    {
        setDuration(duration, duration);
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getDuration(void) const
    {
        return mDurationMin;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setDuration(Real min, Real max)
    {
        mDurationMin = min;
        mDurationMax = max;
        initDurationRepeat();
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setMinDuration(Real min)
    {
        mDurationMin = min;
        initDurationRepeat();
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setMaxDuration(Real max)
    {
        mDurationMax = max;
        initDurationRepeat();
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::initDurationRepeat(void)
    {
        if (mEnabled)
        {
            if (mDurationMin == mDurationMax)
            {
                mDurationRemain = mDurationMin;
            }
            else
            {
                mDurationRemain = Math::RangeRandom(mDurationMin, mDurationMax);
            }
        }
        else
        {
            // Reset repeat
            if (mRepeatDelayMin == mRepeatDelayMax)
            {
                mRepeatDelayRemain = mRepeatDelayMin;
            }
            else
            {
                mRepeatDelayRemain = Math::RangeRandom(mRepeatDelayMax, mRepeatDelayMin);
            }

        }
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setRepeatDelay(Real delay)
    {
        setRepeatDelay(delay, delay);
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getRepeatDelay(void) const
    {
        return mRepeatDelayMin;
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setRepeatDelay(Real min, Real max)
    {
        mRepeatDelayMin = min;
        mRepeatDelayMax = max;
        initDurationRepeat();
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setMinRepeatDelay(Real min)
    {
        mRepeatDelayMin = min;
        initDurationRepeat();
    }
    //-----------------------------------------------------------------------
    void ParticleEmitterEx::setMaxRepeatDelay(Real max)
    {
        mRepeatDelayMax = max;
        initDurationRepeat();
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getMinDuration(void) const
    {
        return mDurationMin;
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getMaxDuration(void) const
    {
        return mDurationMax;
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getMinRepeatDelay(void) const
    {
        return mRepeatDelayMin;    
    }
    //-----------------------------------------------------------------------
    Real ParticleEmitterEx::getMaxRepeatDelay(void) const
    {
        return mRepeatDelayMax;    
    }

    //-----------------------------------------------------------------------
    ParticleEmitterFactoryEx::~ParticleEmitterFactoryEx()
    {
        // Destroy all emitters
        vector<ParticleEmitterEx*>::type::iterator i;
        for (i = mEmitters.begin(); i != mEmitters.end(); ++i)
        {
            OGRE_DELETE (*i);
        }
            
        mEmitters.clear();

    }
    //-----------------------------------------------------------------------
    void ParticleEmitterFactoryEx::destroyEmitter(ParticleEmitterEx* e)        
    {
        vector<ParticleEmitterEx*>::type::iterator i;
        for (i = mEmitters.begin(); i != mEmitters.end(); ++i)
        {
            if ((*i) == e)
            {
                mEmitters.erase(i);
                OGRE_DELETE e;
                break;
            }
        }
    }

    //-----------------------------------------------------------------------

}

